package com.oocl.euc.core.maintenance.service;

import com.oocl.euc.core.maintenance.model.FunctionAuth;
import com.oocl.euc.core.maintenance.model.Role;
import com.oocl.euc.core.maintenance.model.Status;
import com.oocl.euc.core.maintenance.repository.FunctionAuthRepository;
import com.oocl.euc.core.maintenance.repository.RoleRepository;
import com.oocl.euc.core.maintenance.repository.StatusRepository;
import com.oocl.euc.core.maintenance.vo.FunctionAuthEditVO;
import com.oocl.euc.framework.base.persistence.IBaseRepository;
import com.oocl.euc.framework.base.persistence.oracle.SequenceIDPersistable;
import com.oocl.euc.framework.base.service.BaseService;
import com.oocl.euc.framework.common.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class FunctionAuthService extends BaseService<FunctionAuth, Long> {

    @Autowired
    private FunctionAuthRepository functionAuthRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private StatusRepository statusRepository;

    @Override
    protected IBaseRepository<FunctionAuth, Long> getRepository() {
        return functionAuthRepository;
    }


    protected RoleRepository getRoleRepository() {
        return roleRepository;
    }

    protected StatusRepository getStatusRepository() {
        return statusRepository;
    }

    protected FunctionAuthRepository getFunctionAuthRepository() {
        return functionAuthRepository;
    }


    @Transactional
    public List<FunctionAuth> addFunctionAuth(FunctionAuthEditVO functionAuthEditVO) {
        List<Role> roleList = new ArrayList<>();
        List<Status> statusList = new ArrayList<>();
        List<FunctionAuth> functionAuthList = new ArrayList<>();
        List<Long> roleIds = new ArrayList<>();
        List<Long> statusIds = new ArrayList<>();
        Long functionId = functionAuthEditVO.getFunctionId();
        if (CollectionUtils.isNotEmpty(functionAuthEditVO.getRolesSelectionList())) {
            roleList = roleRepository.findRoleByRoleNameIn(functionAuthEditVO.getRolesSelectionList());
            roleIds = roleList.stream().map(SequenceIDPersistable::getId).collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(functionAuthEditVO.getStatusSelectionList())) {
            statusList = statusRepository.findStatusesByStatusNameIn(functionAuthEditVO.getStatusSelectionList());
            statusIds = statusList.stream().map(SequenceIDPersistable::getId).collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(roleList) && CollectionUtils.isNotEmpty(statusList)) {
            functionAuthList = getFunctionAuthListWhenRoleListAndStatusListAreNotEmpty(roleList, statusList, functionId, roleIds, statusIds);

        } else {
            if (CollectionUtils.isNotEmpty(roleList)) {
                functionAuthList = getFunctionAuthListWhenRoleListIsNotEmpty(roleList, functionId, roleIds);
            }
            if (CollectionUtils.isNotEmpty(statusList)) {
                functionAuthList = getFunctionAuthListWhenStatusListIsNotEmpty(statusList, functionId, statusIds);
            }
        }

        functionAuthRepository.saveAll(functionAuthList);
        return functionAuthList;
    }

    private List<FunctionAuth> getFunctionAuthListWhenRoleListAndStatusListAreNotEmpty(List<Role> roleList, List<Status> statusList, Long functionId, List<Long> roleIds, List<Long> statusIds) {
        List<FunctionAuth> functionAuthsWithRoleAndStatus = functionAuthRepository.findByFunctionIdAndRoleIdListAndStatusIdList(functionId, roleIds, statusIds);
        List<FunctionAuth> functionAuthList = new ArrayList<>();

        if (!CollectionUtils.isEmpty(functionAuthsWithRoleAndStatus)) {
            getFunctionAuthListWhenHaveDuplicateRoleAndStatus(roleList, statusList, functionAuthList, functionId, functionAuthsWithRoleAndStatus);
        } else {
            for (Role role : roleList) {
                for (Status status : statusList) {
                    functionAuthList.add(new FunctionAuth(functionId, role, status));
                }
            }
        }
        return functionAuthList;
    }


    private void getFunctionAuthListWhenHaveDuplicateRoleAndStatus(List<Role> roleList, List<Status> statusList, List<FunctionAuth> functionAuthList, Long functionId, List<FunctionAuth> functionAuthsWithRoleAndStatus) {
        List<FunctionAuth> functionAuthWhenExist = new ArrayList<>();
        for (Role role : roleList) {
            for (Status status : statusList) {
                functionAuthList.add(new FunctionAuth(functionId, role, status));
            }
        }
        functionAuthList.stream().<Consumer<? super FunctionAuth>>map(functionAuth -> f -> {
            if (functionAuth.getStatus().getId() == f.getStatus().getId() && functionAuth.getRole().getId() == f.getRole().getId()) {
                functionAuthWhenExist.add(functionAuth);
            } else {
                return;
            }
        }).forEach(functionAuthsWithRoleAndStatus::forEach);
        functionAuthList.removeAll(functionAuthWhenExist);
    }


    private List<FunctionAuth> getFunctionAuthListWhenRoleListIsNotEmpty(List<Role> roleList, Long functionId, List<Long> roleIds) {
        List<FunctionAuth> functionAuthOnlyWithRole = functionAuthRepository.findByFunctionIdAndRoleIdListWithoutStatusIdList(functionId, roleIds);
        List<FunctionAuth> functionAuthList;
        List<FunctionAuth> functionAuthWhenExist = new ArrayList<>();
        functionAuthList = roleList.stream().map(role -> new FunctionAuth(functionId, role)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(functionAuthOnlyWithRole)) {
            functionAuthList.stream().<Consumer<? super FunctionAuth>>map(functionAuth -> f -> {
                if (functionAuth.getRole().getId() == f.getRole().getId()) {
                    functionAuthWhenExist.add(functionAuth);
                } else {
                    return;
                }
            }).forEach(functionAuthOnlyWithRole::forEach);
            functionAuthList.removeAll(functionAuthWhenExist);
        }
        return functionAuthList;
    }

    private List<FunctionAuth> getFunctionAuthListWhenStatusListIsNotEmpty(List<Status> statusList, Long functionId, List<Long> statusIds) {
        List<FunctionAuth> functionAuthOnlyWithStatus = functionAuthRepository.findByFunctionIdAndStatusIdListWithoutRoleIdList(functionId, statusIds);
        List<FunctionAuth> functionAuthList;
        List<FunctionAuth> functionAuthWhenExist = new ArrayList<>();
        functionAuthList = statusList.stream().map(status -> new FunctionAuth(functionId, status)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(functionAuthOnlyWithStatus)) {
            functionAuthList.stream().<Consumer<? super FunctionAuth>>map(functionAuth -> f -> {
                if (functionAuth.getStatus().getId() == f.getStatus().getId()) {
                    functionAuthWhenExist.add(functionAuth);
                } else {
                    return;
                }
            }).forEach(functionAuthOnlyWithStatus::forEach);
            functionAuthList.removeAll(functionAuthWhenExist);
        }
        return functionAuthList;
    }
}
